// Copyright (c) 2010 SuccessFactors, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above
//       copyright notice, this list of conditions and the following
//       disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
//     * Neither the name of the SuccessFactors, Inc. nor the names of
//       its contributors may be used to endorse or promote products
//       derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package org.owasp.jxt;

import java.io.UnsupportedEncodingException;

/**
 * UrlBuilder
 *
 * @author Jeffrey Ichnowski
 * @version $Revision: 8 $
 */
public class UrlBuilder {
    private StringBuilder _url;
    private Encoder _uriComponentEscape;
    private char _separator;
    private boolean _appendedAnchor;

    public UrlBuilder(String base) {
        this(base, Encoder.URI_COMPONENT);
    }

    public UrlBuilder(String base, String encoding) throws UnsupportedEncodingException {
        this(base, Encoder.forURIComponent(
                 (encoding != null) ? encoding : "UTF-8"));
    }

    private UrlBuilder(String base, Encoder uriEscape) {
        _url = new StringBuilder();
        _uriComponentEscape = uriEscape;
        _url.append(base);
        _separator = base.indexOf('?') == -1 ? '?' : '&';
    }

    public final UrlBuilder append(String key, String value) {
        if (_appendedAnchor) {
            throw new IllegalStateException(
                "Additional url parameters may not be appended after the anchor.");
        }
        _url.append(_separator);
        _separator = '&';
        _uriComponentEscape.apply(_url, key);
        _url.append('=');
        _uriComponentEscape.apply(_url, value);
        return this;
    }

    /**
     * <p>Appends an anchor to the URL.  Once an anchor has been
     * appended, attempting to append additional key/value pairs will
     * throw an exception.</p>
     *
     * <p>The rules for escaping the anchor are not entirely clear.
     * The specification indicates that non-ascii characters in the
     * anchor are invalid.  To deal with the possibility of non-ASCII
     * characters, we URL encode everything after the parameter.  This
     * has the side-effect of requiring the recipient of the anchor
     * (e.g. JavaScript code, the link itself, etc...) to also handle
     * URL encoding.  The main problem with this is anything that was
     * expecting "%" to be left alone after the hash mark, will then
     * see it as %25.</p>
     *
     * <ol>
     * <li>HTML 4.01 Spec, 12.2.1 Syntax of anchor names: "Anchor names
     * should be restricted to ASCII characters."</li>
     * <li>HTML 4.01 Spec, B.2.1 Non-ASCII characters in URI attribute
     * values: "1. Represent each character in UTF-8 ... 2. Escape
     * these bytes with the URI escaping mechanism ..."</li>
     * </ol>
     */
    public final UrlBuilder anchor(String anchor) {
        if (!_appendedAnchor) {
            _url.append('#');
            _appendedAnchor = true;
        }
        Encoder.URI.apply(_url, anchor);
        return this;
    }

    public final String toString() {
        return _url.toString();
    }
} // UrlBuilder
